<?php
/**
 * 爱员工实名认证接口
 * 本接口用来调试用户入职合同的实名认证信息
 */
namespace Service\CCTApplyfor;
use Exception\ApiException;
use Util\Log;
use \libx\Manager\DI;
class RealNameAuthentication extends \Service\BaseService
{

    /**
     * RSA算法类
     * 签名及密文编码：base64字符串/十六进制字符串/二进制字符串流
     *
     * Notice:Only accepts a single block. Block size is equal to the RSA key size!
     * 如密钥长度为1024 bit，则加密时数据需小于128字节，加上PKCS1Padding本身的11字节信息，所以明文需小于117字节
     */

    private static $_instance;
    //rsa文件路径
    private $rsaPath = null;
    //对方的公钥
    private $pubKey = null;
    //我们的私钥
    private $priKey = null;


    public static function getInstance($public_key_file, $private_key_file)
    {
        if (!(self::$_instance instanceof self)) {
            self::$_instance = new self($public_key_file, $private_key_file);
        }
        return self::$_instance;
    }


    /**
     * 构造函数
     *
     * @param string 公钥文件（验签和加密时传入）
     * @param string 私钥文件（签名和解密时传入）
     */
    public function __construct($public_key_file = '', $private_key_file = '')
    {
        $this->rsaPath    = \Yaf\Registry::get('appConf')['application']['system']['rsa'];
        if (!APP_ONLINE) {
            $this->rsaPath =  $this->rsaPath.'/dev';
        }else{
            $this->rsaPath =  $this->rsaPath.'/product';
        }

        $public_key_file  = $this->rsaPath .'/'. $public_key_file;
        $private_key_file = $this->rsaPath .'/'. $private_key_file;

        if ($public_key_file) {
            $this->_getPublicKey($public_key_file);
        }
        if ($private_key_file) {
            $this->_getPrivateKey($private_key_file);
        }
    }

    /**
     * 自定义错误处理
     */
    private function _error($msg)
    {
        die('RSA Error:' . $msg); //TODO
    }


    /**
     * 检测填充类型
     * 加密只支持PKCS1_PADDING
     * 解密支持PKCS1_PADDING和NO_PADDING
     * @param int 填充模式
     * @param string 加密en/解密de
     * @return bool
     */
    function _checkPadding($padding, $type)
    {
        if ($type == 'en') {
            switch ($padding) {
                case OPENSSL_PKCS1_PADDING:
                    $ret = true;
                    break;
                default:
                    $ret = false;
            }
        } else {
            switch ($padding) {
                case OPENSSL_PKCS1_PADDING:
                case OPENSSL_NO_PADDING:
                    $ret = true;
                    break;
                default:
                    $ret = false;
            }
        }
        return $ret;
    }

    /**
     * 加密
     */
    private function _encode($data, $code)
    {
        switch (strtolower($code)) {
            case 'base64':
                $data = base64_encode('' . $data);
                break;
            case 'hex':
                $data = bin2hex($data);
                break;
            case 'bin':
            default:
        }
        return $data;
    }

    /**
     * 解密
     */
    private function _decode($data, $code)
    {
        switch (strtolower($code)) {
            case 'base64':
                $data = base64_decode($data);
                break;
            case 'hex':
                $data = $this->_hex2bin($data);
                break;
            case 'bin':
            default:
        }
        return $data;
    }

    /**
     * 初始化公钥
     */
    private function _getPublicKey($file)
    {
        $key_content = $this->_readFile($file);
        if ($key_content) {
            $this->pubKey = openssl_get_publickey($key_content);
        }
    }


    /**
     * 初始化私钥
     */
    private function _getPrivateKey($file)
    {
        $key_content = $this->_readFile($file);
        if ($key_content) {
            $this->priKey = openssl_get_privatekey($key_content);
        }
    }


    /**
     * 读取私钥文件
     */
    private function _readFile($file)
    {
        $ret = false;
        if (!file_exists($file)) {
            $this->_error("The file {$file} is not exists");
        } else {
            $ret = file_get_contents($file);
        }
        return $ret;
    }
    private function _hex2bin($hex = false)
    {
        $ret = $hex !== false && preg_match('/^[0-9a-fA-F]+$/i', $hex) ? pack("H*", $hex) : false;
        return $ret;
    }


    /**
     * 生成签名
     *
     * @param string 签名材料
     * @param string 签名编码（base64/sha256）
     * @return 签名值
     */
    public function sign($data, $code = 'base64')
    {
        $ret = false;
        if (openssl_sign($data, $ret, $this->priKey, OPENSSL_ALGO_SHA256)) {
            $ret = $this->_encode($ret, $code);
        }
        return $ret;
    }

    /**
     * 验证签名
     *
     * @param string 签名材料
     * @param string 签名值
     * @param string 签名编码（base64/hex/bin）
     * @return bool
     */
    public function verify($data, $sign, $code = 'base64')
    {
        $ret = false;
        $sign = $this->_decode($sign, $code);

        if ($sign !== false) {
            switch (openssl_verify($data, $sign, $this->pubKey, OPENSSL_ALGO_SHA256)) {
                case 1:
                    $ret = true;
                    break;
                case 0:
                case -1:
                default:
                    $ret = false;
            }
        }
        return $ret;
    }

    /**
     * 加密
     * @param string 明文
     * @param string 密文编码（base64/hex/bin）
     * @param int 填充方式（貌似php有bug，所以目前仅支持OPENSSL_PKCS1_PADDING）
     * @return string 密文
     */
    public function encrypt($data, $code = 'base64', $padding = OPENSSL_PKCS1_PADDING)
    {
        $ret = false;
        if (!$this->_checkPadding($padding, 'en')) $this->_error('padding error');

        if (openssl_public_encrypt($data, $result, $this->pubKey, $padding)) {
            $ret = $this->_encode($result, $code);
        }
        return $ret;
    }

    /**
     * 解密
     * @param string 密文
     * @param string 密文编码（base64/hex/bin）
     * @param int 填充方式（OPENSSL_PKCS1_PADDING / OPENSSL_NO_PADDING）
     * @param bool 是否翻转明文（When passing Microsoft CryptoAPI-generated RSA cyphertext, revert the bytes in the block）
     * @return string 明文
     */
    public function decrypt($data, $code = 'base64', $padding = OPENSSL_PKCS1_PADDING, $rev = false)
    {
        $ret = false;
        $data = $this->_decode($data, $code);
        if (!$this->_checkPadding($padding, 'de')) $this->_error('padding error');
        if ($data !== false) {
            if (openssl_private_decrypt($data, $result, $this->priKey, $padding)) {
                $ret = $rev ? rtrim(strrev($result), "\0") : '' . $result;
            }
        }
        return $ret;
    }


    /**
     * 数组转字符串 主要适配爱员工的签名字符串的转变
     * 逻辑:1.对关联数组按键进行递归的排序 ksourt();
     * 2. 对关联数组的key=>value 转换成 key1=value1&key2=value2(我们的关联数组在JAVA看来就是对象)
     */
    public function key_sourt($data)
    {
        if (!is_array($data)) {
            return false;
        }
        //按照规则生成签名串
        $str = $this->key_sourt_toString($data, '');
        //echo $str;
        //生成签名
        $data['sign'] =  $this->sign($str);
        $data['signType'] = "RSA";
        return $data;
    }

    /**
     * 设置爱员工前置授权的 extrSystemId
     * @param $platform
     * @return string
     */
    public static function getAygExtrSystemId($platform){
        //爱员工分配给企业应用的AppId
        switch ($platform) {
            case 14:
                return "chuchujie";
                break;
            case \Util\Constant::PLATFORM_TTX:
                return "tiantianxuan";
                break;
            case \Util\Constant::PLATFORM_MRYT:
                return "meiriyitao";
                break;
            default:
                return "chuchujie";
        }
    }

    /**
     * 获取合同模板Id
     * @param $platform
     * @return int
     */
    public static function getSygTemplateId($platform){
        if (!APP_ONLINE) {
            //爱员工分配给企业应用的AppId
            switch ($platform) {
                case 14:    //楚楚推
                    return 267;
                    break;
                    break;
                default:
                    return 267;
            }
        }else{
            switch ($platform) {
                case 14:
                    return 248;
                    break;
                default:
                    return 0;
            }
        }
    }

    /**
     *只支持关联索引的结构
     * key1=value1&key2={k1=v1&k2=v2…}&key3=[value3,value32]&key4=[{key41=value41&key42=value42},
     * {key41=value411&key42=value422}]
     */
    public static function key_sourt_toString($data, $result)
    {
        global $result;
        if (!is_array($data)) {
            return false;
        }

        foreach($data as $key => $val){
            if($val === ''){
                if(empty($val)){//echo $key."\n";
                    unset($data[$key]);
                }
            }
            if(is_null($val)){
                unset($data[$key]);
            }
        }

        $count = count($data);
        $i = 0;
        ksort($data);

        foreach ($data as $key => $val) {
            $i++;
            if (is_numeric($key)) {
                return false;
            }
            if (!is_array($val)) {
                //指针到达数组最后一个元素
                if ($i == $count) {
                    $result .= $key . '=' . $val;
                } else {
                    $result .= $key . '=' . $val . '&';
                }
            } else {
                ksort($val);
                $result .= $key . '={';
                self::key_sourt_toString($val, $result);
                $result .= '}&';
            }
        }
        $result = rtrim($result, '&');
        return $result;
    }



    /**
     *生成随机字符串
     */
    public static function create_rand_str($pw_length =  4)
    {
        $randpwd = '';
        for ($i = 0; $i < $pw_length; $i++) {
            $randpwd .= chr(mt_rand(33, 126));
        }
        return $randpwd;
    }

    /**
     * 爱员工实名认证的DEMO
     */
    public static function aYgSmrzdemo(){
        $url = 'https://hmjsjopenapi.aiyuangong.net/prepare92/asyn/certification';
        $postData = array(
            'name'   => '张三',                   //姓名
            'idcard' => '130429198607041012',    //身份证号
            'mobile' => '18301005105',           //认证手机号
            'payAccountType' => 'BANK_CARDNO',   //第三方收款账号类型:银行卡号, ALIPAY_LOGONID 支付宝登录号，支持邮箱和手机号格式；
            'payAccount' => '12312312313',       //收款账号,如银行卡号，支付宝登陆号等，根据payAccountType进行填写。
            'notifyUrl'  => 'http://gaga.com',   //回掉地址
            'validType'  => 3,                    //3三要素认证（姓名+身份证+银行卡）
            'extrSystemId' =>self::getAygExtrSystemId(14),
            'timestamp' => time(),
            'nonce' => self::create_rand_str(4),
            'requestId' => 780137

        );

        $public_key_file  = 'ayg_dev_rsa_public_key.pem';
        $private_key_file = 'ccj_dev_ayg_rsa_private_key.pem';
        $rsa =  RealNameAuthentication::getInstance($public_key_file, $private_key_file);

        $platform = 14;
        $cckUid = 123;
        $data = $rsa->key_sourt($postData,$platform,$cckUid);
        print_r($data);

        $client = DI::get('http');
        $client->setTimeout(4000,4000);
        $client->setHeader(array('Content-Type:application/json'));
        $response = $client->post($url, json_encode($data));
        print_r($response);

    }


    /**
     * 爱员工签名反验证DEMO
     */
    public static function aYgsingvali(){
        $public_key_file = 'ayg_dev_rsa_public_key.pem';
        $private_key_file = 'ccj_dev_ayg_rsa_private_key.pem';
        $rsa =  RealNameAuthentication::getInstance($public_key_file, $private_key_file);

        $str = '{"code":"0000","data":{"nonce":"_0&9","timestamp":"2018-04-25 10:50:28","signType":"RSA","requestId":"123","sign":"e1pVkwZpl+sTI3V8lWVghGAdzQLx1Rhb6Q2LCL2hvB/F9kn7JL2JpsToHrgb/Rwfz3bxM+zC88KAq2jTJ8zWuxqq8bNT36WAhfkYSH3e40CgXwLEOecom5/BwV8o2i5pD/eAhStE8P07IC7rr7UOMv11bDShrCaaSqq7XtmS9Zg=","certResult":"0","certResultMsg":"实名认证中"}}';

        $respons = json_decode($str);
        $respons =self::object_to_array($respons);
        //print_r($respons);
        $response = $respons['data'];
        $sign = $response['sign'];
        unset($response['sign']);       //不参与验证
        unset($response['signType']);   //不参与验证

        $str = self::key_sourt_toString($response, '');
        $res = $rsa->verify($str, $sign);
        //***var_dump($res);
    }

    /**
     * 对象 转 数组
     * @param object $obj 对象
     * @return array
     */
    public static  function object_to_array($obj) {
        $obj = (array)$obj;
        foreach ($obj as $k => $v) {
            if (gettype($v) == 'resource') {
                return;
            }
            if (gettype($v) == 'object' || gettype($v) == 'array') {
                $obj[$k] = (array)self::object_to_array($v);
            }
        }
        return $obj;
    }

    /**
     * 数组 转 对象
     * @param array $arr 数组
     * @return object
     */
    public static function array_to_object($arr) {
        if (gettype($arr) != 'array') {
            return;
        }
        foreach ($arr as $k => $v) {
            if (gettype($v) == 'array' || getType($v) == 'object') {
                $arr[$k] = (object)self::array_to_object($v);
            }
        }
        return (object)$arr;
    }
}
